﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Brain.Rendering
{
    public class DefaultRenderManager2D : IRenderManager2D
    {
        public GraphicsDevice GraphicsDevice { get; set; }

        public SpriteBatch SpriteBatch;

        public bool ShouldDraw { get; set; }

        public Color BackBufferColor;

        public List<ISprite> Sprites;

        public SpriteSortMode SortMode;
        public BlendState BlendState;

        public Texture2D Empty;

        public Matrix? CustomResolutionMatrix { get; protected set; }

        public DefaultRenderManager2D(GraphicsDevice device)
        {
            this.GraphicsDevice = device;

            this.SpriteBatch = new SpriteBatch(this.GraphicsDevice);

            this.ShouldDraw = true;

            BackBufferColor = Color.Coral;

            Sprites = new List<ISprite>();

            SortMode = SpriteSortMode.Immediate;

            BlendState = BlendState.AlphaBlend;

            Empty = new Texture2D(device, 1, 1);
            Empty.SetData(new Color[] { Color.White });
        }
        public virtual void Reset()
        {
            if (!this.SpriteBatch.IsDisposed)
            {
                this.SpriteBatch.Dispose();
            }
            Sprites.Clear();

            this.SpriteBatch = new SpriteBatch(this.GraphicsDevice);
            this.Sprites = new List<ISprite>();
        }

        /// <summary>
        /// Automatically resizes your game rendering to a new resolution. TAKE CARE, this method will
        /// make a Matrix that WILL strecth everything to match the resolution, maintaining the Aspect
        /// Ratio. This is a WIP, use carefully
        /// </summary>
        /// <param name="LastResHeight">The Height the Game has been made to work</param>
        /// <param name="LastResWidth">The Width the Game has been made to work</param>
        /// <param name="NewResHeight">The Height the Game will assume as a new resolution</param>
        /// <param name="NewResWidth">The Width the Game will assume as a new resolution</param>
        public void UseCustomResolution(int LastResWidth, int LastResHeight,
            int NewResWidth, int NewResHeight)
        {
            if (LastResWidth != NewResWidth &&
                LastResHeight != NewResHeight)
            {
                GraphicsDevice device = Engine.Instance.GraphicsDevice;
                PresentationParameters pp = device.PresentationParameters;

                float xRatio = LastResWidth / (float)NewResWidth;
                float yRatio = LastResHeight / (float)NewResHeight;
                float ratio = Math.Max(xRatio, yRatio);// The greatest still
                pp.BackBufferWidth = (int)(LastResWidth / ratio);
                pp.BackBufferHeight = (int)(LastResHeight / ratio);

                Viewport view = device.Viewport;
                view.Width = pp.BackBufferWidth;
                view.Height = pp.BackBufferHeight;

                int presWidth = device.PresentationParameters.BackBufferWidth;
                int presHeight = device.PresentationParameters.BackBufferHeight;
                int X = (int)((presWidth / 2f) - (pp.BackBufferWidth / 2f));
                int Y = (int)((presHeight / 2f) - (pp.BackBufferHeight / 2f));
                if (X > Y)
                {
                    Y = 0;
                }
                else
                {
                    X = 0;
                }
                view.X = X;
                view.Y = Y;
                //CustomResViewport = view;
                device.Viewport = view;

                float FactorX = pp.BackBufferWidth / (float)LastResWidth;
                float FactorY = pp.BackBufferHeight / (float)LastResHeight;

                float Factor2X = LastResWidth / (float)pp.BackBufferWidth;
                float Factor2Y = LastResHeight / (float)pp.BackBufferHeight;
                StartResolutionFactor = new Vector2(Factor2X, Factor2Y);

                CustomResolutionFactor = new Vector2(FactorX, FactorY);
                CustomResolutionMatrix = Matrix.CreateScale(new Vector3(CustomResolutionFactor, 1));

                //ReturnResolutionMatrix = Matrix.CreateScale(new Vector3(StartResolutionFactor, 1));

                //StartResHeight = LastResHeight;
                //StartResWidth = LastResWidth;

                //TapesSize = new Vector2(view.X, view.Y);
            }
        }
        Vector2 CustomResolutionFactor;
        Vector2 StartResolutionFactor;

        public virtual void Draw()
        {
            GraphicsDevice.Clear(BackBufferColor);
        }

        public virtual void EndDraw()
        {
            if (Sprites.Count > 0)
            {
                StartSpriteBatch(SpriteBatch, null);
                IEffected last = null;
                for (int i = 0; i < Sprites.Count; i++)
                {
                    ISprite sprite = Sprites[i];
                    bool drawed = false;

                    Vector2 origin = Vector2.Zero;
                    if (sprite.Origin != null)
                    {
                        origin = sprite.Origin.Value;
                    }

                    if (sprite is IEffected)
                    {
                        IEffected ie = sprite as IEffected;
                        if (ie.Effect != null)
                        {
                            if (last == null || ie.Effect != last.Effect)
                            {
                                last = ie;
                                SpriteBatch.End();

                                StartSpriteBatch(SpriteBatch, ie.Effect);
                            }
                        }
                        else
                        {
                            if (last != null)
                            {
                                SpriteBatch.End();

                                StartSpriteBatch(SpriteBatch, null);
                            }

                            last = null;
                        }
                    }
                    else
                    {
                        if (last != null)
                        {
                            SpriteBatch.End();

                            if (Engine.Instance.CameraManager.Camera2D != null)
                            {
                                SpriteBatch.Begin(SortMode, BlendState, null, null, null, null, Engine.Instance.CameraManager.Camera2D.World);
                            }
                            else
                            {
                                SpriteBatch.Begin(SortMode, BlendState, null, null, null, null, Matrix.Identity);
                            }
                        }

                        last = null;
                    }

                    if (sprite is ITextured)
                    {
                        ITextured tex = sprite as ITextured;
                        if (tex.Texture != null)
                        {
                            if (tex.Texture.IsDisposed)
                            {
                                SpriteBatch.End();
                                return;
                            }
                            if (tex is IShadowed)
                            {
                                IShadowed shadowed = tex as IShadowed;
                                if (shadowed.Shadow != 0)
                                {
                                    SpriteBatch.Draw(tex.Texture, RectangleUtil.Add(sprite.BoundsRectangle, new Rectangle(shadowed.Shadow, shadowed.Shadow, 0, 0)),
                                        tex.SourceRectangle, Color.Black, sprite.RotationAngle, origin, SpriteEffects.None, 0);
                                }
                            }
                            SpriteBatch.Draw(tex.Texture, sprite.BoundsRectangle, tex.SourceRectangle, sprite.Color, sprite.RotationAngle, origin, sprite.SpriteEffects, 0);
                            drawed = true;
                        }
                    }
                    if (sprite is ISpriteFonted)
                    {
                        ISpriteFonted fonted = sprite as ISpriteFonted;
                        if (fonted.SpriteFont != null && !string.IsNullOrEmpty(fonted.Text))
                        {
                            drawed = true;
                            if (fonted is IShadowed)
                            {
                                IShadowed shadowed = fonted as IShadowed;
                                if (shadowed.Shadow != 0)
                                {
                                    SpriteBatch.DrawString(fonted.SpriteFont, fonted.Text,
                                        new Vector2(sprite.BoundsRectangle.X + shadowed.Shadow,
                                            sprite.BoundsRectangle.Y + shadowed.Shadow),
                                            Color.Black, sprite.RotationAngle, origin, 1f, SpriteEffects.None, 0);
                                }
                            }
                            SpriteBatch.DrawString(fonted.SpriteFont, fonted.Text, new Vector2(sprite.BoundsRectangle.X, sprite.BoundsRectangle.Y),
                                sprite.Color, sprite.RotationAngle, origin, 1f, sprite.SpriteEffects, 0);

                        }
                    }
                    if (sprite is IBounded)
                    {
                        IBounded bounded = sprite as IBounded;
                        if (bounded.BoundsRectangleColor != null)
                        {
                            if (bounded is IShadowed)
                            {
                                IShadowed shadowed = bounded as IShadowed;
                                if (shadowed.Shadow != 0)
                                {
                                    SpriteBatch.Draw(Empty, RectangleUtil.Add(bounded.BoundsRectangle, new Rectangle(2, 2, 0, 0)), Color.Black);
                                }
                            }
                            SpriteBatch.Draw(Empty, bounded.BoundsRectangle, bounded.BoundsRectangleColor.Value);
                        }
                    }

                    if (!drawed && Engine.Instance.Editor != null)
                    {
                        SpriteBatch.Draw(Empty, sprite.BoundsRectangle, new Color(255, 255, 255, 127));
                    }
                }
                SpriteBatch.End();
            }
        }

        private void StartSpriteBatch(SpriteBatch batch, Effect effect)
        {
            if (Engine.Instance.CameraManager.Camera2D != null)
            {
                if (CustomResolutionMatrix != null)
                {
                    SpriteBatch.Begin(SortMode, BlendState, null, null, null, effect, Engine.Instance.CameraManager.Camera2D.World
                        * CustomResolutionMatrix.Value);
                }
                else
                {
                    SpriteBatch.Begin(SortMode, BlendState, null, null, null, effect, Engine.Instance.CameraManager.Camera2D.World);
                }
            }
            else
            {
                if (CustomResolutionMatrix != null)
                {
                    SpriteBatch.Begin(SortMode, BlendState, null, null, null, effect, CustomResolutionMatrix.Value);
                }
                else
                {
                    SpriteBatch.Begin(SortMode, BlendState, null, null, null, effect, Matrix.Identity);
                }
            }
        }

        public virtual void StartDraw(GameScreen gs)
        {
            
        }

        public virtual void FinishDraw(GameScreen gs)
        {
        }

        public void SubmitSprite(ISprite sprite)
        {
            Sprites.Add(sprite);
        }

        public void RemoveSprite(ISprite sprite)
        {
            Sprites.Remove(sprite);
        }


        public SerializationData Data { get; set; }

        public void SerializeData(DefaultSerializationManager manager)
        {
            Data = new SerializationData();
            Data.AddData("DFRM2D.BackBufferColor", this.BackBufferColor);
        }

        public void DeserializeData(DefaultSerializationManager manager, SerializationData newData)
        {
            this.BackBufferColor = newData.GetData<Color>("DFRM2D.BackBufferColor");
        }





        public ISprite Editor_GetSprite(int index)
        {
            return Sprites[index];
        }
        public int Editor_Count()
        {
            return Sprites.Count;
        }
    }
}
